Skip to content

fix(query-devtools): sanitize invalid browser locales#10321

Open
jakvbs wants to merge 3 commits intoTanStack:mainfrom
jakvbs:fix/query-devtools-invalid-locale
Open

fix(query-devtools): sanitize invalid browser locales#10321
jakvbs wants to merge 3 commits intoTanStack:mainfrom
jakvbs:fix/query-devtools-invalid-locale

Conversation

@jakvbs
Copy link

@jakvbs jakvbs commented Mar 23, 2026

What

  • sanitize invalid browser locale values before devtools formatting uses them
  • wrap the query devtools UI in a Kobalte I18nProvider with the sanitized locale and route mutation date formatting through the sanitized locale helpers
  • add regression tests for invalid navigator.language values like "undefined"
  • add a patch changeset for @tanstack/query-devtools

Why

  • @tanstack/query-devtools can currently throw at runtime when the browser reports an invalid locale string, for example navigator.language === "undefined"
  • the invalid locale can surface through bundled dependency locale resolution as well as devtools' own locale-sensitive date formatting paths

How tested

  • npx nx run @tanstack/query-devtools:test:eslint
  • npx nx run @tanstack/query-devtools:test:types
  • npx nx run @tanstack/query-devtools:test:lib
  • npx nx run @tanstack/query-devtools:build
  • npx nx run @tanstack/query-devtools:test:build
  • manual repro against the linked issue reproduction app using Playwright-managed Chromium 145.0.7632.6 and Firefox 146.0.1; verified the unfixed published packages throw and the patched local packages no longer do

Fixes #10286.

Summary by CodeRabbit

  • New Features

    • Devtools now uses an i18n-aware locale provider so timestamps display according to the current locale.
    • Date/time displays in mutation and query panels use consistent locale-aware formatting.
  • Bug Fixes

    • Invalid or unsupported browser locales are sanitized and now safely fall back to English (US).
  • Tests

    • Added tests covering locale selection, sanitization, and date/time formatting.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 23, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 09246576-62b8-4bd7-bded-6afc28857275

📥 Commits

Reviewing files that changed from the base of the PR and between def64a7 and 60e08a3.

📒 Files selected for processing (3)
  • packages/query-devtools/src/Devtools.tsx
  • packages/query-devtools/src/__tests__/locale.test.ts
  • packages/query-devtools/src/utils.tsx
✅ Files skipped from review due to trivial changes (1)
  • packages/query-devtools/src/tests/locale.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/query-devtools/src/utils.tsx

📝 Walkthrough

Walkthrough

Added locale-safety utilities and integrated a reactive, sanitized locale signal via an I18nProvider into the devtools UI; replaced direct toLocale* calls with formatDateTime/formatTime and added tests and a changeset.

Changes

Cohort / File(s) Summary
Locale utilities
packages/query-devtools/src/utils.tsx
Added DEFAULT_LOCALE, NavigatorWithUserLanguage, sanitizeLocale(), getPreferredLocale(), createSafeLocale() and formatDateTime()/formatTime() to normalize locale values, handle languagechange, and safely format dates/times.
Devtools locale integration
packages/query-devtools/src/DevtoolsComponent.tsx, packages/query-devtools/src/DevtoolsPanelComponent.tsx
Create a safe locale signal with createSafeLocale() and wrap the component tree with I18nProvider so children receive the locale context.
Locale-aware UI updates
packages/query-devtools/src/Devtools.tsx
Replaced direct toLocaleString()/toLocaleTimeString() usages with formatDateTime(...)/formatTime(...) and added useLocale() consumption in UI components to render timestamps using the sanitized locale.
Tests & changelog
packages/query-devtools/src/__tests__/locale.test.ts, .changeset/safe-languages-wash.md
Added unit tests validating locale sanitization and formatting resilience; added a changeset documenting the locale sanitization behavior.

Sequence Diagram

sequenceDiagram
    participant Browser
    participant DevtoolsComponent
    participant I18nProvider
    participant Devtools
    participant LocaleSignal as locale()
    participant Formatter as formatDateTime/formatTime

    Browser->>DevtoolsComponent: mount
    DevtoolsComponent->>DevtoolsComponent: createSafeLocale() -> locale signal
    DevtoolsComponent->>I18nProvider: provide locale(locale())
    I18nProvider->>Devtools: render children with locale context
    Devtools->>LocaleSignal: useLocale()
    LocaleSignal-->>Devtools: current sanitized locale
    Devtools->>Formatter: formatDateTime(timestamp, locale)
    Formatter-->>Devtools: formatted string
    Browser->>DevtoolsComponent: languagechange event
    DevtoolsComponent->>LocaleSignal: update locale signal
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 I hopped through strings both odd and loose,

Replaced the "undefined" with gentler truce.
Now clocks and dates all safely hum,
Locale-sorted, tidy, and plum. 🌸

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix(query-devtools): sanitize invalid browser locales' clearly and concisely describes the main change—sanitizing invalid locale values in query-devtools.
Description check ✅ Passed The PR description comprehensively covers what changed, why it was needed, and how it was tested, aligning well with the repository's template structure.
Linked Issues check ✅ Passed All coding requirements from issue #10286 are met: locale sanitization, safe fallback to 'en-US', locale-sensitive formatting through sanitized helpers, and regression tests for invalid values.
Out of Scope Changes check ✅ Passed All changes are directly in scope—adding locale sanitization utilities, integrating I18nProvider, updating date formatting calls, and including regression tests with changeset documentation.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/query-devtools/src/Devtools.tsx (2)

2053-2058: ⚠️ Potential issue | 🟡 Minor

Inconsistent: QueryDetails still uses unsafe toLocaleTimeString().

This code path can still throw a RangeError if the browser reports an invalid locale. For consistency with the mutation formatting changes, consider using the safe locale helper here as well.

Suggested fix

Add useLocale() to QueryDetails and update the formatting:

 const QueryDetails = () => {
+  const locale = useLocale()
   const theme = useTheme()
   // ...
             <span>
-              {new Date(activeQueryState()!.dataUpdatedAt).toLocaleTimeString()}
+              {formatDateTime(activeQueryState()!.dataUpdatedAt, locale.locale())}
             </span>

Note: You may want a formatTime helper if you prefer time-only output, or adjust formatDateTime to accept a format option.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/query-devtools/src/Devtools.tsx` around lines 2053 - 2058,
QueryDetails currently calls new
Date(activeQueryState()!.dataUpdatedAt).toLocaleTimeString() which can throw a
RangeError for invalid locales; update QueryDetails to use the safe locale
helper by importing and calling useLocale() (or retrieving the locale via
existing hook) and then format the timestamp with the existing formatDateTime or
a new formatTime helper instead of toLocaleTimeString; replace the direct
Date.toLocaleTimeString call with
formatDateTime(activeQueryState()!.dataUpdatedAt, { locale }) or
formatTime(activeQueryState()!.dataUpdatedAt, locale), and if needed adjust
formatDateTime signature to accept a time-only option.

2495-2502: ⚠️ Potential issue | 🟡 Minor

Inconsistent: MutationDetails still uses unsafe toLocaleTimeString().

Same issue as in QueryDetails. This "Submitted At" display can throw if the browser has an invalid locale.

Suggested fix
 const MutationDetails = () => {
+  const locale = useLocale()
   const theme = useTheme()
   // ...
             <span>
-              {new Date(
-                activeMutation()!.state.submittedAt,
-              ).toLocaleTimeString()}
+              {formatDateTime(activeMutation()!.state.submittedAt, locale.locale())}
             </span>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/query-devtools/src/Devtools.tsx` around lines 2495 - 2502, The
"Submitted At" render in Devtools.tsx uses new
Date(activeMutation()!.state.submittedAt).toLocaleTimeString(), which can throw
for invalid locales; update the MutationDetails rendering to format the date
safely by wrapping the toLocaleTimeString call in a try/catch (or using
Intl.DateTimeFormat with a safe fallback) and falling back to a stable
representation like toISOString() or a hardcoded locale (e.g., 'en-US') if
formatting fails; locate the code around activeMutation(), state.submittedAt and
replace the unsafe toLocaleTimeString usage with the safe formatter and fallback
logic so the component never throws when rendering timestamps.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@packages/query-devtools/src/Devtools.tsx`:
- Around line 2053-2058: QueryDetails currently calls new
Date(activeQueryState()!.dataUpdatedAt).toLocaleTimeString() which can throw a
RangeError for invalid locales; update QueryDetails to use the safe locale
helper by importing and calling useLocale() (or retrieving the locale via
existing hook) and then format the timestamp with the existing formatDateTime or
a new formatTime helper instead of toLocaleTimeString; replace the direct
Date.toLocaleTimeString call with
formatDateTime(activeQueryState()!.dataUpdatedAt, { locale }) or
formatTime(activeQueryState()!.dataUpdatedAt, locale), and if needed adjust
formatDateTime signature to accept a time-only option.
- Around line 2495-2502: The "Submitted At" render in Devtools.tsx uses new
Date(activeMutation()!.state.submittedAt).toLocaleTimeString(), which can throw
for invalid locales; update the MutationDetails rendering to format the date
safely by wrapping the toLocaleTimeString call in a try/catch (or using
Intl.DateTimeFormat with a safe fallback) and falling back to a stable
representation like toISOString() or a hardcoded locale (e.g., 'en-US') if
formatting fails; locate the code around activeMutation(), state.submittedAt and
replace the unsafe toLocaleTimeString usage with the safe formatter and fallback
logic so the component never throws when rendering timestamps.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9d0f7d4f-a138-42ad-8a09-2a83e6a41225

📥 Commits

Reviewing files that changed from the base of the PR and between b1188fe and 171bf85.

📒 Files selected for processing (5)
  • packages/query-devtools/src/Devtools.tsx
  • packages/query-devtools/src/DevtoolsComponent.tsx
  • packages/query-devtools/src/DevtoolsPanelComponent.tsx
  • packages/query-devtools/src/__tests__/locale.test.ts
  • packages/query-devtools/src/utils.tsx

@afurm
Copy link

afurm commented Mar 23, 2026

Nice fix. One observation on sanitizeLocale in utils.tsx: Intl.DateTimeFormat.supportedLocalesOf() does not throw on invalid locale tags - it returns an empty array. So the try/catch around it is effectively dead code in any environment that supports Intl.DateTimeFormat.supportedLocalesOf. The || fallback on the result already handles the invalid-locale case correctly.

The try/catch would only matter if you wanted to guard against environments where Intl.DateTimeFormat.supportedLocalesOf itself is undefined, but at that point the whole devtools locale feature would be broken anyway. Worth removing it to keep the code honest, or adding a comment explaining what it guards against.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

@tanstack/query-devtools: invalid navigator.language values like "undefined" cause a RangeError at runtime

2 participants